﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Context;
using NHibernate.Cfg;

namespace WebPattern.DataAccess
{
    /// <summary>
    /// Session defines the context and the scope of an interaction between an application and the underlying database
    /// </summary>
    public static class NHibernateSessionManager
    {
        private static readonly object _factorySyncRoot = new object();
        private static ISessionFactory _sessionFactory;
        private static string _hibernateCfgPath;

        public static ISessionFactory ConfigureFromFile(string hibernateCfgPath)
        {
            if (hibernateCfgPath == null)
                throw new ArgumentNullException("hibernateCfgPath");

            if (_sessionFactory != null)
                throw new InvalidOperationException("SessionFactory is already configured");

            lock (_factorySyncRoot)
                if (_sessionFactory == null)
                {
                    _hibernateCfgPath = hibernateCfgPath;

                    var cfg = new Configuration();
                    cfg.Configure(hibernateCfgPath);
                    cfg.AddAssembly(typeof(IRepository).Assembly);
                    //var export = new SchemaExport(cfg);
                    //export.Execute(false, true, false);
                    _sessionFactory = cfg.BuildSessionFactory();
                }

            return _sessionFactory;
        }

        public static ISessionFactory GetSessionFactory()
        {
            if (_sessionFactory == null)
                lock (_factorySyncRoot)
                    if (_sessionFactory == null)
                    {
                        var cfg = new Configuration();
                        if (!string.IsNullOrWhiteSpace(_hibernateCfgPath)) cfg.Configure(_hibernateCfgPath);
                        cfg.AddAssembly(typeof(IRepository).Assembly);
                        //cfg.AddAssembly(typeof(IRepository).Assembly);
                        _sessionFactory = cfg.BuildSessionFactory();
                    }
            return _sessionFactory;
        }

        public static IStatelessSession OpenStatelessSession()
        {
                return GetSessionFactory().OpenStatelessSession();
        }

        public static ISession OpenSession()
        {
            return GetSessionFactory().OpenSession();
        }

        public static ISession OpenSessionTransaction()
        {
            var session = GetSessionFactory().OpenSession();
            session.BeginTransaction();
            return session;
        }

        public static ISession GetCurrentSession()
        {
            if (!CurrentSessionContext.HasBind(_sessionFactory))
            {
                return BindSession();
            }
            else
            {
                return GetSessionFactory().GetCurrentSession();
            }
        }

        public static ISession BindSession()
        {
            var session = OpenSession();
            CurrentSessionContext.Bind(session);
            return session;
        }

        public static ITransaction BeginTransaction()
        {
            return GetCurrentSession().BeginTransaction();
        }

        public static ITransaction GetCurrentTransaction()
        {
            return GetCurrentSession().Transaction;
        }

        public static void UnbindSession(bool forceTransactionRollback)
        {
            var session = CurrentSessionContext.Unbind(_sessionFactory);
            if (session != null)
            {
                var tx = session.Transaction;
                try
                {
                    if (tx != null)
                    {

                        if (tx.WasRolledBack)
                            return;

                        if (forceTransactionRollback)
                        {
                            tx.Rollback();
                            return;
                        }

                        if (tx.IsActive && tx.WasCommitted == false)
                        {
                            session.Flush();
                            tx.Commit();
                        }

                    }
                    else
                    {
                        session.Flush();
                    }
                }
                catch
                {
                    if (tx != null)
                        tx.Rollback();
                    throw;
                }
                finally
                {
                    session.Dispose();
                }
            }
        }

        public static void UnbindSession()
        {
            UnbindSession(false);
        }
    }
    
    ///// <summary>
    ///// Session defines the context and the scope of an interaction between an application and the underlying database
    ///// </summary>
    //public static class NHibernateSessionManager
    //{
        
    //    private static readonly object syncRoot = new object();
    //    private static ISessionFactory sessionFactory;


    //    public static ISession CurrentSession
    //    {
    //        get
    //        {
    //            return GetSessionFactory().GetCurrentSession();
    //        }
    //    }


    //    public static void BindSession()
    //    {
    //        var session = GetSessionFactory().OpenSession();
    //        var transaction = session.BeginTransaction();
    //        CurrentSessionContext.Bind(session);
    //    }

    //    public static void UnbindSession()
    //    {
    //        var session = CurrentSessionContext.Unbind(GetSessionFactory());
    //        if (session != null)
    //        {
    //            ITransaction transaction = session.Transaction;
    //            if (transaction != null)
    //            {
    //                if (transaction.IsActive && transaction.WasCommitted == false)
    //                {
    //                    session.Flush();
    //                    transaction.Commit();
    //                }
    //            }
    //            else
    //                session.Flush();
    //        }

    //        session.Close();
    //        session.Dispose();  
    //    }

    //    public static void RollbackTransaction()
    //    {
    //        var tx = CurrentSession.Transaction;
    //        if (tx != null && tx.IsActive)
    //            tx.Rollback();
    //    }


    //    public static ISessionFactory GetSessionFactory()
    //    {
    //        if (sessionFactory == null)
    //        {
    //            lock (syncRoot)
    //            {
    //                if (sessionFactory == null)
    //                {
    //                    var cfg = new Configuration();
    //                    cfg.AddAssembly(typeof(IRepository).Assembly);
    //                    sessionFactory = cfg.BuildSessionFactory();
    //                }
    //            }
    //        }

    //        return sessionFactory;
    //    }
    //}
}
